# gozerlib/persist/persist.py
#
#

"""
     allow data to be written to disk in JSON format. creating the persisted 
    object restores data. 
"""

## gozerlib imports

from gozerlib.utils.trace import whichmodule, calledfrom
from gozerlib.utils.lazydict import LazyDict
from gozerlib.utils.exception import handle_exception
from gozerlib.utils.name import stripname
## simplejson imports

from waveapi.simplejson import load, dump, loads, dumps

## google imports

from google.appengine.ext import db
from google.appengine.api.memcache import get, set, replace
from google.appengine.api.datastore_errors import Timeout

# basic imports
import pickle
import thread
import os
import copy
import sys
import logging
import types
import time

class JSONindb(db.Model):

    """ model to store json files in. """

    modtime = db.DateTimeProperty(auto_now=True)
    createtime = db.DateTimeProperty(auto_now_add=True)
    filename = db.StringProperty()
    content = db.TextProperty(indexed=False)

class Persist(object):

    """
        persist data attribute to database backed JSON file. 

        :param filename: filename to store data in
        :type filename: string
        :param default: default data incase Persist object is not yet initialised
        :type default: dict, list, string, integer or None
        :rtype: None

    """

    def __init__(self, filename, default={}):
        self.plugname = calledfrom(sys._getframe())

        if 'gozerlib' in self.plugname:
            self.plugname = calledfrom(sys._getframe(1))

        self.fn = filename.strip() # filename to save to
        self.key = db.Key.from_path("JSONindb", self.fn)
        logging.debug("persist - %s - key is %s" % (self.fn, str(self.key)))
        #self.obj = LazyDict()
        self.obj = None
        jsontxt = get(self.fn)

        if type(default) == types.DictType:
            default2 = LazyDict()
            default2.update(default)
        else:
            default2 = copy.deepcopy(default)

        if jsontxt is None:
            logging.debug("persist - filename is %s" % self.fn)

            try:
                try:
                    self.obj = db.get(self.key)
                except Timeout:
                    self.obj = db.get(self.key)
                #self.obj = JSONindb.get_by_key_name(self.fn)
            except Exception, ex:
                handle_exception()
                self.data = default2
                return

            if self.obj == None:
                logging.debug('persist - %s - creating new object in init' % self.fn)
                self.obj = JSONindb(key=self.key)
                self.obj.content = unicode(default)
                self.data = default2
                return

            jsontxt = self.obj.content
            gotcache = False
            set(self.fn, jsontxt)

        else:
            gotcache = True


        self.data = loads(jsontxt)

        if type(self.data) == types.DictType:
            d = LazyDict()
            d.update(self.data)
            self.data = d

        cfrom = whichmodule()

        if 'gozerlib' in cfrom:
            cfrom = whichmodule(1)
            if 'gozerlib' in cfrom:
                cfrom = whichmodule(2)

        if gotcache:
            logging.debug('persist - %s - loaded %s (%s) *cache*' % (cfrom, self.fn, len(jsontxt)))
        else:
            logging.debug('persist - %s - loaded %s (%s)' % (cfrom, self.fn, len(jsontxt)))

    def listall(self):

        """ list all available json files in database. """

        json = JSONindb.all()
        j = json.fetch(1000) 
        names = []

        for i in j:
            names.append(i.__key__)

        return names

    def save(self):

        """ save json data to database. """

        bla = dumps(self.data)

        if self.obj == None:
            logging.debug('persist - %s - creating new object for saving' % self.fn)
            self.obj = JSONindb(key_name=self.fn)
            self.obj.content = bla
        else:
            self.obj.content = bla
        self.obj.filename = self.fn
        logging.debug('persist - object is %s' % str(self.obj))        
        key = self.obj.put()  
        logging.debug('persist - %s - createtime is %s' % (self.fn, self.obj.createtime))
        set(self.fn, bla)
        logging.warn('saved %s (%s)' % (self.fn, len(bla)))

class PlugPersist(Persist):

    """
        persist plug related data. data is stored in gozerstore/plugs/{plugname}/{filename}

        :param filename: filename to store data in
        :type filename: string
        :param default: default data incase Persist object is not yet initialised
        :type default: dict, list, string, integer or None
        :rtype: None

    """

    def __init__(self, filename, default=None):

        # retrieve plugname where object is constructed
        plugname = calledfrom(sys._getframe())

        # call base constructor with appropiate filename
        Persist.__init__(self, 'gozerstore' + os.sep + 'plugs' + os.sep + plugname + os.sep + filename)

